/*
 * xmega_uart.c
 *
 *  Created on: 2011/05/08
 *      Author: AKS
 */

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>

#include "core/include/aks_processor.h"
#include "core/include/types.h"

#include "chip/include/chip.h"




/* ------------------------------------------------------------------------- *
 * General Define for Xmega CPU
 * ------------------------------------------------------------------------- */
// for CTRLB register
#define RXEN 0x10
#define TXEN 0x08
#define CLK2X 0x04
#define MPCM 0x02
#define TXB8 0x01

// for STATUS
#define DREIF 0x20

// functions for xmega USART
BOOL Init_UART_F(WORD baudrate);
BOOL UART_F_setReceiveCallback(UART_RCV_CALLBACK callback);
BOOL UART_F_transmitBytes(BYTE *buf, BYTE len);
BOOL Init_UART_C(WORD baudrate);
BOOL UART_C_setReceiveCallback(UART_RCV_CALLBACK callback);
BOOL UART_C_transmitBytes(BYTE *buf, BYTE len);


// simple callback
void UART_RX_pass_to_PORTF(BYTE data)
{
	UART_F_transmitBytes( &data, 1 );

	return;
}


/* ------------------------------------------------------------------------- *
 * UART in Port F is used for Debug port
 * ------------------------------------------------------------------------- */
UART_RCV_CALLBACK uartF_callback;

BOOL Init_UART_F(WORD baudrate)
{
	BYTE sysClkMHz = getSystemClockMHz();


	// baudrate switch
	WORD rateSetting = 0xFFFF;
	if( sysClkMHz == 32)
	{
		switch((DWORD)baudrate)
		{
		case 115200:
			rateSetting = 0x417;
			break;
		case  57600:
			rateSetting = 0x86E;
			break;
		case  38400:
			rateSetting = 0xCC5;
			break;
		default:
			break;
		}
	}
	else if(sysClkMHz == 16)
	{
		switch((DWORD)baudrate)
		{
		case 115200:
			rateSetting = 0x1EC;
			break;
		case  57600:
			rateSetting = 0x417;
			break;
		case  38400:
			rateSetting = 0x643;
			break;
		default:
			break;
		}
	}

	if(rateSetting != 0xFFFF)
	{
	    USARTF0.BAUDCTRLA = (BYTE)(rateSetting & 0xff);
	    USARTF0.BAUDCTRLB = 0xA0 | (BYTE)((rateSetting>>8) & 0xf);
	}
	else
	{
		return FALSE;
	}

	// need for update baudrate
	volatile DWORD i = 0;
	while(++i < 10000);

	/* set PORTF IO */
	PORTF.DIRSET = 0x08;
	PORTF.DIRCLR = 0x04;

	/* uart configuration */
	USARTF0.CTRLA = USART_RXCINTLVL_HI_gc | USART_TXCINTLVL_OFF_gc | USART_DREINTLVL_OFF_gc;
	USARTF0.CTRLB = RXEN | TXEN;
	USARTF0.CTRLC = USART_CMODE_ASYNCHRONOUS_gc | USART_PMODE_DISABLED_gc | USART_CHSIZE_8BIT_gc;

    /* set default call back */
    uartF_callback = UART_RX_pass_to_PORTF;

    return TRUE;
}
BOOL UART_F_setReceiveCallback(UART_RCV_CALLBACK callback)
{
	uartF_callback = callback;

	return TRUE;
}

BOOL UART_F_transmitBytes(BYTE *buf, BYTE len)
{
	if(len <= 0)
	{
		return FALSE;
	}

    while(len--)
    {
    	while( !(USARTF0.STATUS & DREIF) );
        USARTF0.DATA = *buf;
        buf++;
    }

	return TRUE;
}

ISR(USARTF0_RXC_vect)
{
xmega_mem_enter_ISR();

    uartF_callback( USARTF0.DATA );

xmega_mem_exit_ISR();

	return;
}

/* ------------------------------------------------------------------------- *
 * UART in Port C is used for Syb System port
 * ------------------------------------------------------------------------- */
UART_RCV_CALLBACK uartC_callback;

BOOL Init_UART_C(WORD baudrate)
{
    uartC_callback = UART_RX_pass_to_PORTF;

    return TRUE;
}

BOOL UART_C_setReceiveCallback(UART_RCV_CALLBACK callback)
{
	uartC_callback = callback;

	return TRUE;
}


BOOL UART_C_transmitBytes(BYTE *buf, BYTE len)
{
	return TRUE;
}

/* ------------------------------------------------------------------------- *
 * UART package
 * ------------------------------------------------------------------------- */
typedef struct _UART_PACKAGE
{
    BOOL (*init_uart)(WORD);
    BOOL (*setRcvCallback)(UART_RCV_CALLBACK);
    BOOL (*transmitBytes)(BYTE *, BYTE);
} UART_PACKAGE;

#define N_UART_PORTS    2
UART_PACKAGE uarts[N_UART_PORTS] =
{
    // debug port
	{
        Init_UART_F,
        UART_F_setReceiveCallback,
        UART_F_transmitBytes
	},
    // sub system port
	{
        Init_UART_C,
        UART_C_setReceiveCallback,
        UART_C_transmitBytes
	}
};

ISR(USARTC0_RXC_vect)
{
xmega_mem_enter_ISR();

    uartC_callback( USARTC0.DATA );

xmega_mem_exit_ISR();

	return;
}

/* ------------------------------------------------------------------------- *
 * for adopter to AKS core System
 * ------------------------------------------------------------------------- */
BOOL Init_UART(UART_ID portID, WORD baudrate)
{
    if(portID < N_UART_PORTS)
    {
        return uarts[portID].init_uart(baudrate);
    }
    else
    {
    	return FALSE;
    }
}


BOOL UART_setReceiveCallback(UART_ID portID, UART_RCV_CALLBACK callback)
{
    if(portID < N_UART_PORTS)
    {
        return uarts[portID].setRcvCallback(callback);
    }
    else
    {
    	return FALSE;
    }
}


BOOL UART_transmitBytes(UART_ID portID, BYTE *buf, BYTE len)
{
    if(portID < N_UART_PORTS)
    {
        return uarts[portID].transmitBytes(buf, len);
    }
    else
    {
    	return FALSE;
    }
}

BOOL UART_transmitByte(UART_ID portID, BYTE buf)
{
    if(portID < N_UART_PORTS)
    {
        return uarts[portID].transmitBytes(&buf, 1);
    }
    else
    {
    	return FALSE;
    }
}


BOOL UART_transmitString(UART_ID portID, BYTE *buf)
{
    if(portID < N_UART_PORTS)
    {
        while(*buf != '\0')
        {
            uarts[portID].transmitBytes(buf, 1);
            buf++;
        }

        return TRUE;
    }
    else
    {
    	return FALSE;
    }
}
